Kotlin Grammars

Table of Contents

1. Basic Grammars

  • Variables. Variables declared by val is immutable, while declared by var is mutable.
  • Function definition. Use the fun keyword, all types are post
  • String Templates. Just like Python, "${variable}" has the same meaning.
  • Null types. To let a variable be nullable, add ? after its type, eg., val v : String? = null
    • Safe calls. suppose a.b?.c?.d, if at any time, the property b,c does not exist, the function returns null instead of raising error.
    • Elvis operator. If LHS is null, this operator returns the RHS. nullString?.length ?: 0

1.1. Basic Collections

  • List.
  • Set, unordered and only stores unique items.
    • to create, use setOf(xx, yy, zz) or mutableSetOf(xx, yy, zz)
    • to check existence, use xx in set
    • to add or remove item, use .add() and .remove() respectively
  • Map, stores key-value pairs.
    • to create, use mapOf(k1 to v1, k2 to v2) or mutableMapOf(...) syntax
    • to access value through key, use map[key] syntax. If the key does not exist, then null is returned
    • to add items, [] operator can also be used
    • to remove items, use .remove(key) method
    • to check key existence, use .containsKey()
    • to get collections of keys or values, use .keys and .values respectively

1.2. Control Flow

  • if is similar to all PLs
  • when is similar to switch - case or match - with flows as in other languages
  val obj = "Hello"

  when (obj) {
      "1" -> println("One")
      "Hello" -> println("Greeting")
      else -> println("Unknown")
  }

  var result = when (obj) {
      "1" -> "one"
      else -> "unknown"
  }
  • for(value in collection) just like other languages
    • Ranges. 1..4 means 1,2,3,4, while 1..<4 means 1,2,3. To declare in reversed order, use 4 downTo 1 which is 4,3,2,1. To declare with step, use 1..5 step 2 which is 1,3,5.
  • while() loop is same as other languages

1.3. Lambda Functions

Lambda functions are wrapped by {}, the syntax is

  val function = { arg: Type -> /* expression */ }

2. Intermediate Kotlin Grammars

2.1. Extension Function

Usually, we can define methods inside the definition of a class.

  class HttpClient {
      fun request(method: String, url: String, headers: Map<String, String>): HttpResponse {
    // ......
      }
  }

Sometimes, we want to add methods to some existing classes. For example, specification of get() and post() method:

  fun HttpClient.get(url: String): HttpResponse = request("GET", url, emptyMap())
  fun HttpClient.post(url: String): HttpResponse = request("POST", url, emptyMap())

The syntax is fun <receiver_class>.<method>(<args>): <return_type> = <body>. This style is also called extension-oriented design.

2.2. Scope Functions

In Kotlin, some scope functions allow us to create a temporary scope around an object and execute some code. Very similar to if let in Rust.

2.2.1. let function

The let function creates a scope that we can refer to the receiver with it keyword. Use the grammar sugar if we want to perform null checks and later perform further actions with that object.

  val address: String? = ...;
  val confirm = address?.let {
      doSomething(it)
  }

  // which is equivalent to
  val address: String? = ...;
  val confirm = if (address != null) {
      doSomething(address)
  } else { null }

2.2.2. apply function

Use apply scope function to initialize objects, e.g., class instances, at the time of creation rather than later in the code.

  val client = Client().apply {
      token = "asdf"
      connect()
      authenticate()
  }

  fun main() {
      client.getData()
  }

2.2.3. run function

Similar to apply, but used when a result needs to be computed.

  val client = Client().apply {
      token = "asdf"
  }

  fun main() {
      val result = client.run {
    connect()
    authenticate()
    getData()
      }
  }

2.2.4. also function

Use also to complete an additional action with the object. This object is returned for further computation.

  fun main() {
      val medals: List<String> = listOf("Gold", "Silver", "Bronze")
      val reversedLongUppercaseMedals: List<String> =
          medals
              .map { it.uppercase() }
              .also { println(it) }
              // [GOLD, SILVER, BRONZE]
              .filter { it.length > 4 }
              .also { println(it) }
              // [SILVER, BRONZE]
              .reversed()
      println(reversedLongUppercaseMedals)
      // [BRONZE, SILVER]
  }

2.2.5. with function

Use with if we want to call multiple functions on the object.

  val mainMonitorSecondaryBufferBackedCanvas = Canvas()
  with(mainMonitorSecondaryBufferBackedCanvas) {
      text(10, 10, "Foo")
      rect(20, 30, 100, 50)
      circ(40, 60, 25)
      text(15, 45, "Hello")
      rect(70, 80, 150, 100)
      circ(90, 110, 40)
      text(35, 55, "World")
      rect(120, 140, 200, 75)
      circ(160, 180, 55)
      text(50, 70, "Kotlin")
  }

2.3. Lambda expression with receivers

We can make lambda function accept a receiver to let it access member functions and properties. Its syntax is ClassName.[ ... ] where [ ... ] denotes the lambda function body.

2.4. Classes and Interfaces

By default, all classes are final, i.e., cannot be inherited. However, abstract classes can be inherited. An abstract class can have abstract property and abstract methods, whose implementation should be provided by subclasses, with explicit override keyword.

In Kotlin, each class can only have 1 parent, but can implement multiple interfaces. In short, abstract class uses “is-a” logic, while interfaces uses “can-do” logic.

2.4.1. Delegation

Suppose an interface DrawingTool and an already implemented class PenTool. Delegation is that when implementing CanvasTool, we directly reuse code from other implemented classes. Traditionally, if we want to do this, we have to declare each function

  class CanvasSession(
      val tool: DrawingTool
  ) : DrawingTool {

      override fun draw() =
          tool.draw()

      override fun erase() =
          tool.erase()

      override fun getInfo() =
          tool.getInfo()
  }

With by keyword, we can get rid of duplicated code. The compiler will auto-generate code for us. Or we can override a few functions.

  class CanvasSession(
      val tool: DrawingTool
  ) : DrawingTool by tool

  class CanvasSession(
      val tool: Drawingtool
  ) : DrawingTool by tool {
      override val color = "blue"
  }

2.5. TODO Object

Kotlin’s built-in support for singletons.

2.5.1. Data Objects

2.5.2. Companion Objects

Date: 2026-05-29 Fri 00:00